pacman::p_load(readxl, ggiraph, plotly, patchwork, DT, tidyverse, gifski, gapminder, gganimate) Interactive and Animated Data Visualization

1. Learning Objectives
Create interactive data visualization using
ggiraph,plotlyr,crosstalkCreate animated data visualization using
gganimate,gifski,gapminder
2. Load Packages
The following R packages will be used: - DT provides an R interface to the JavaScript library DataTables that create interactive table on html page - tidyverse is a family of R packages designed to support data science, analysis and communication task including creating static statistical graphs - patchwork combines multiple ggplot2 graphs into one figure
Interactive data visualisation: - ggiraph makes ggplot graphics interactive - plotly plots interactive statistical graphs
Animated data visualization: gganimate, an ggplot extension for creating animated statistical graphs. gifski converts video frames to GIF animations using pngquant’s fancy features for efficient cross-frame palettes and temporal dithering. It produces animated GIFs that use thousands of colors per frame. gapminder: An excerpt of the data available at Gapminder.org. We just want to use its country_colors scheme.
3. Import Data
exam_data is a year end examination grades of a cohort of Primary 3 students from a local school.
exam_data <- read.csv("data/Exam_data.csv")
glimpse(exam_data)Rows: 322
Columns: 7
$ ID <chr> "Student321", "Student305", "Student289", "Student227", "Stude…
$ CLASS <chr> "3I", "3I", "3H", "3F", "3I", "3I", "3I", "3I", "3I", "3H", "3…
$ GENDER <chr> "Male", "Female", "Male", "Male", "Male", "Female", "Male", "M…
$ RACE <chr> "Malay", "Malay", "Chinese", "Chinese", "Malay", "Malay", "Chi…
$ ENGLISH <int> 21, 24, 26, 27, 27, 31, 31, 31, 33, 34, 34, 36, 36, 36, 37, 38…
$ MATHS <int> 9, 22, 16, 77, 11, 16, 21, 18, 19, 49, 39, 35, 23, 36, 49, 30,…
$ SCIENCE <int> 15, 16, 16, 31, 25, 16, 25, 27, 15, 37, 42, 22, 32, 36, 35, 45…
globalPop
col <- c("Country", "Continent")
globalPop <- read_xls("data/GlobalPopulation.xls",
sheet="Data") %>%
mutate(across(col, as.factor)) %>%
mutate(Year = as.integer(Year))
glimpse(globalPop)Rows: 6,204
Columns: 6
$ Country <fct> "Afghanistan", "Afghanistan", "Afghanistan", "Afghanistan",…
$ Year <int> 1996, 1998, 2000, 2002, 2004, 2006, 2008, 2010, 2012, 2014,…
$ Young <dbl> 83.6, 84.1, 84.6, 85.1, 84.5, 84.3, 84.1, 83.7, 82.9, 82.1,…
$ Old <dbl> 4.5, 4.5, 4.5, 4.5, 4.5, 4.6, 4.6, 4.6, 4.6, 4.7, 4.7, 4.7,…
$ Population <dbl> 21559.9, 22912.8, 23898.2, 25268.4, 28513.7, 31057.0, 32738…
$ Continent <fct> Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia,…
4. Interactive Data Visualization using R Package: ggiraph
ggiraph is is a tool that allows you to create dynamic ggplot graphs. It allows the addition of tooltips, hover effects and JavaScript actions to the graphics. The package also allows the selection of graphical elements when used in shiny applications.
ggiraph is an htmlwidget and a ggplot2 extension. The graphs are exported as SVG documents and special attributes are used on various elements.
The htmlwidgets package provides a framework for creating R bindings to JavaScript libraries. HTML Widgets can be: (1) used at the R console for data analysis just like conventional R plots, (2) embedded within R Markdown documents, (3) ncorporated into Shiny web applications.
Interactive is made with ggplot geometries, legends and theme elements with three aesthetics:
tooltip: Tooltips to be displayed when mouse is over elementsonclick: JavaScript function to be executed when elements are clickeddata_id: ID associated with elements for hover and click actions
Usage:
- Provide at least one of the aesthetics
tooltip,data_idandonclickto create interactive elements. - Call function
girafe()with the ggplot object so that the graphic is translated as a web interactive graphics.
We can replace
geom_point()bygeom_point_interactive()geom_sf()bygeom_sf_interactive()
4.1 Tooltip
To plot an interactive statistical graph with one attributes ID by using ggiraph package,
Create an ggplot object using
ggplot()andgeom_dotplot_interactive()Indiate
aes()to include an attributetooltip = IDCreate an interactive svg object using
girafe()to be displayed on an html page
Show the code
plot1 <- ggplot(data = exam_data,
aes(x = MATHS))+
geom_dotplot_interactive(aes(tooltip = ID),
stackgroups = TRUE,
binwidth = 1,
method = "histodot") +
scale_y_continuous(NULL, breaks = NULL)+
theme_minimal()+
theme(legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_line(colour = "grey"))
girafe(
ggobj = plot1,
width_svg = 6,
height_svg = 6*0.618
)paste0() concatenate strings or vectors, in this example, two attributes, namely ID and CLASS. Note the change in aes() to tooltip = exam_data$tooltip
Show the code
exam_data$tooltip <- c(paste0("Name = ", exam_data$ID,
"\n Class = ", exam_data$CLASS))
plot2 <- ggplot(data = exam_data,
aes(x = MATHS))+
geom_dotplot_interactive(aes(tooltip = exam_data$tooltip),
stackgroups = TRUE,
binwidth = 1,
method = "histodot") +
scale_y_continuous(NULL, breaks = NULL)+
theme_minimal()+
theme(legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_line(colour = "grey"))
girafe(
ggobj = plot2,
width_svg = 6,
height_svg = 6*0.618
)The customization is indicated by tooltip_css below, which changes the background-colour to white and the font colour to black and font-style bold.
opts_tooltip() within giraph() customize the tooltip rendering by add css declarations.
Arguments of opts_tooltip(): css = NULL, offx = 10, offy = 0, use_cursor_pos = TRUE, opacity = 0.9, use_fill = FALSE, use_stroke = FALSE, delay_mouseover = 200, delay_mouseout = 500, placement = c(“auto”, “doc”, “container”), zindex = 999
Show the code
tooltip_css <- "background-color: white; font-style: bold; color: black"
exam_data$tooltip <- c(paste0("Name = ", exam_data$ID,
"\n Class = ", exam_data$CLASS))
plot3 <- ggplot(data = exam_data,
aes(x = MATHS))+
geom_dotplot_interactive(aes(tooltip = exam_data$tooltip),
stackgroups = TRUE,
binwidth = 1,
method = "histodot") +
scale_y_continuous(NULL, breaks = NULL)+
theme_minimal()+
theme(legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_line(colour = "grey"))
girafe(
ggobj = plot3,
width_svg = 6,
height_svg = 6*0.618,
options = list(opts_tooltip(css = tooltip_css))
)Show the code
exam_data$Average_Score <- round(rowMeans(exam_data[, c("ENGLISH", "MATHS", "SCIENCE")]),
digits = 2)
# tooltip_css <- "background-color: white; font-style: bold; color: black"
exam_data$tooltip2 <- paste0("Name = ", exam_data$ID,
"\nClass = ", exam_data$CLASS,
"\nEnglish = ", exam_data$ENGLISH,
"\nMaths = ", exam_data$MATHS,
"\nScience = ", exam_data$SCIENCE,
"\nAverage Score = ", exam_data$Average_Score)
plot4 <- ggplot(data = exam_data,
aes(x = Average_Score))+
geom_dotplot_interactive(aes(tooltip = exam_data$tooltip2),
stackgroups = TRUE,
binwidth = 1,
method = "histodot") +
scale_y_continuous(NULL, breaks = NULL)+
theme_minimal()+
theme(legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_line(colour = "grey"))
girafe(
ggobj = plot4,
width_svg = 6,
height_svg = 6*0.618,
options = list(opts_tooltip(css = tooltip_css))
)The top student is Student036 from Class 3A!
stat_summary() operates on unique x or y to create visualizations that display summary metrics.
Show the code
tooltip <- function(y, ymax, accuracy = 0.01) {
mean <- scales::number(y, accuracy = accuracy)
sem <- scales::number(ymax - y, accuracy = accuracy)
paste("Mean maths scores:", mean, "+/-", sem)
}
plot5 <- ggplot(data = exam_data,
aes(x = RACE))+
stat_summary(aes(y = MATHS, tooltip = after_stat(tooltip(y, ymax))),
fun.data = "mean_se", geom = GeomInteractiveCol, fill = "lightblue")+
stat_summary(aes(y = MATHS),
fun.data = mean_se, geom = "errorbar", width = 0.2, size = 0.2)+
theme_minimal()+
theme(legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_line(colour = "grey"))
girafe(ggobj = plot5,
width_svg = 6,
height_svg = 6*0.618,
options = list(opts_tooltip(css = tooltip_css)))4.2 Hover Effect
To create the hover effect, data_id() is specified as an aes() argument in the interactive geom functions. Note the change in aes() to data_id = CLASS from the previous example.
Elements associated with a data_id (i.e CLASS) will be highlighted upon mouse over.
Show the code
plot6 <- ggplot(data=exam_data,
aes(x = MATHS))+
geom_dotplot_interactive(aes(data_id = CLASS),
stackgroups = TRUE,
binwidth = 1,
method = "histodot")+
scale_y_continuous(NULL, breaks = NULL)+
theme_minimal()+
theme(legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_line(colour = "grey"))
girafe(
ggobj = plot6,
width_svg = 6,
height_svg = 6*0.618
) opts_tooltip(), opts_hover() and opts_hover_inv() customize the tooltip and data_id rendering by add css declarations.
Effect on geometries: opts_hover(css = NULL, reactive = FALSE, nearest_distance = NULL) Effect on other geometries: opts_hover_inv(css = NULL) Interactive scales: opts_hover_key(css = NULL, reactive = FALSE) Interactive theme elements: opts_hover_theme(css = NULL, reactive = FALSE)
The default value of the hover css is hover_css = “fill:orange;”. In this example, we will use fill: #ABEBC6;.
Show the code
plot7 <- ggplot(data = exam_data,
aes(x = MATHS))+
geom_dotplot_interactive(aes(data_id = CLASS),
stackgroups = TRUE,
binwidth = 1,
method = "histodot")+
scale_y_continuous(NULL, breaks = NULL)+
theme_minimal()+
theme(legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_line(colour = "grey"))
girafe(
ggobj = plot7,
width_svg = 6,
height_svg = 6*0.618,
options = list(
opts_hover(css = "fill: #ABEBC6;"),
opts_hover_inv(css = "opacity:0.2;")
)
) opts_hover() and opts_hover_inv() customize the data_id rendering by add css declarations.
Show the code
tooltip_css <- "background-color: white; font-style: bold; color: black"
exam_data$tooltip <- c(paste0("Name = ", exam_data$ID,
"\n Class = ", exam_data$CLASS))
plot8 <- ggplot(data = exam_data,
aes(x = MATHS))+
geom_dotplot_interactive(aes(tooltip = exam_data$tooltip, data_id = CLASS),
stackgroups = TRUE,
binwidth = 1,
method = "histodot")+
scale_y_continuous(NULL, breaks = NULL)+
theme_minimal()+
theme(legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_line(colour = "grey"))
girafe(
ggobj = plot8,
width_svg = 6,
height_svg = 6*0.618,
options = list(
opts_tooltip(css = tooltip_css),
opts_hover(css = "fill: #ABEBC6;"),
opts_hover_inv(css = "opacity:0.2;")
)
) 4.3 Onclick
onclick of girafe provides hot link interactivity on the web. The web document link with a data object will be displayed on the web browser upon mouse click. Note the change in aes() to onclick = onclick where onclick is assigned with web document link.
sprintf() print formatted strings where fmt is the format and x is the value to format.
Show the code
exam_data$onclick <- sprintf("window.open(\"%s%s\")",
"https://www.moe.gov.sg/schoolfinder?journey=Primary%20school",
as.character(exam_data$ID))
plot9 <- ggplot(data = exam_data,
aes(x = MATHS))+
geom_dotplot_interactive(aes(onclick = onclick),
stackgroups = TRUE,
binwidth = 1,
method = "histodot")+
scale_y_continuous(NULL, breaks = NULL)+
theme_minimal()+
theme(legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_line(colour = "grey"))
girafe(
ggobj = plot9,
width_svg = 6,
height_svg = 6*0.618) Note that click actions must be a string column in the dataset containing valid javascript instructions.
4.4 Coordinated Multiple Views
Coordinated view refers to the visualization when a data point of one plot is selected, the corresponding data point ID on the second or associated data visualisations will be highlighted.
The following programming strategy will be used:
- Appropriate interactive functions of
ggiraphwill be used to create the multiple views. patchworkwill be used inside girafe function to create the interactive coordinated multiple views.
Show the code
plot10 <- ggplot(data = exam_data,
aes(x = ENGLISH))+
geom_dotplot_interactive(aes(data_id = ID, tooltip = exam_data$tooltip),
stackgroups = TRUE,
binwidth = 1,
method = "histodot")+
coord_cartesian(xlim = c(0,100))+
labs(title = "ENGLISH", x = NULL)+
scale_y_continuous(NULL, breaks = NULL)+
theme_minimal()+
theme(legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_line(colour = "grey"))
plot11 <- ggplot(data = exam_data,
aes(x = MATHS))+
geom_dotplot_interactive(aes(data_id = ID, tooltip = exam_data$tooltip),
stackgroups = TRUE,
binwidth = 1,
method = "histodot")+
coord_cartesian(xlim = c(0,100))+
labs(title = "MATHS", x = NULL)+
scale_y_continuous(NULL, breaks = NULL)+
theme_minimal()+
theme(legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_line(colour = "grey"))
plot12 <- ggplot(data = exam_data,
aes(x = SCIENCE))+
geom_dotplot_interactive(aes(data_id = ID, tooltip = exam_data$tooltip),
stackgroups = TRUE,
binwidth = 1,
method = "histodot")+
labs(title = "SCIENCE", x = NULL)+
coord_cartesian(xlim = c(0,100))+
scale_y_continuous(NULL, breaks = NULL)+
theme_minimal()+
theme(legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_line(colour = "grey"))
girafe(code = print(plot10 / plot11 / plot12),
width_svg = 6,
height_svg = 3,
options = list(
opts_tooltip(css = tooltip_css),
opts_hover(css = "fill: #ABEBC6;"),
opts_hover_inv(css = "opacity:0.2;")
)
) 5. Interactive Data Visualization using R Package: plotly
plotly is an R package for creating interactive web-based graphs via the open source JavaScript graphing library plotly.js. It creates a variety of interactive graphics.
There are two main ways to creating a plotly object: either by transforming a ggplot2 object ggplotly() into a plotly object or by directly initializing a plotly object with plot_ly()/plot_geo()/plot_mapbox().
Each dot represents a student and the associated english and math score is displayed on the interactive graph.
plot_ly(data = exam_data,
x = ~MATHS, y = ~ENGLISH)color argument is mapped to a qualitative visual variable, in this example, RACE. This representation adds on a broad category to the visualization.
plot_ly(data = exam_data,
x = ~MATHS, y = ~ENGLISH,
color = ~RACE)6. Interactive Data Visualization using R Package: ggplotly
Requires ggplot() and then using ggplotly()
p <- ggplot(data=exam_data,
aes(x = MATHS, y = ENGLISH))+
geom_point(size=1)+
coord_cartesian(xlim=c(0,100),
ylim=c(0,100))+
theme_minimal()+
theme(legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_line(colour = "grey"))
ggplotly(p)p <- ggplot(data=exam_data,
aes(x = MATHS, y = ENGLISH, colour = RACE))+
geom_point(size=1)+
coord_cartesian(xlim=c(0,100),
ylim=c(0,100))+
theme_minimal()+
theme(legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_line(colour = "grey"))
ggplotly(p)The creation of a coordinated linked plot by using plotly involves three steps:
highlight_key()ofplotlypackage is used as shared data.Two scatterplots will be created by using ggplot2 functions.
subplot()ofplotlypackage is used to place them next to each other side-by-side.
d <- highlight_key(exam_data)
p1 <- ggplot(data=d,
aes(x = MATHS, y = ENGLISH,
text = paste("<b>Maths Score:</b>", MATHS,"<br><b>English Score:", ENGLISH))) +
geom_point(size=1, shape = 12) +
coord_cartesian(xlim=c(0,100),
ylim=c(0,100))+
theme_minimal()
p2 <- ggplot(data=d,
aes(x = MATHS, y = SCIENCE,
text = paste("<b>Maths Score:</b>", MATHS,"<br><b>Science Score:", SCIENCE))) +
geom_point(size=1, shape = 5) +
coord_cartesian(xlim=c(0,100),
ylim=c(0,100))+
theme_minimal()
subplot(ggplotly(p1, tooltip = "text"),
ggplotly(p2, tooltip = "text"))7. Interactive Data Visualization using R Package: crosstalk
Crosstalk is an add-on to the htmlwidgets package. It extends htmlwidgets with a set of classes, functions, and conventions for implementing cross-widget interactions (currently, linked brushing and filtering).
Limitations of Crosstalk:
HTML widgets must be specifically modified to support Crosstalk
Only works for linked brushing and filtering of views that show individual data points, not aggregate or summary views
Not appropriate for large data sets
6.1 Data Table
DT::datatable(exam_data[c("ID","CLASS","GENDER","RACE","ENGLISH","MATHS","SCIENCE")],
class= "compact")6.2 Linked Brushing
d <- highlight_key(exam_data)
p <- ggplot(data=d,
aes(x = MATHS, y = ENGLISH,
text = paste("<b>Maths Score:</b>", MATHS,"<br><b>English Score:", ENGLISH))) +
geom_point(size=1, shape = 12) +
coord_cartesian(xlim=c(0,100),
ylim=c(0,100))+
theme_minimal()
gg <- highlight(ggplotly(p),
"plotly_selected")
crosstalk::bscols(gg,
DT::datatable(d),
widths = 5) 8. Animated Data Visualization using gganimate
When creating animations, the plot does not actually move. Instead, many individual plots are built and then stitched together as movie frames. Each frame is a different plot when conveying motion, which is built using some relevant subset of the aggregate data. The subset drives the flow of the animation when stitched back together.
gganimate extends the grammar of graphics as implemented by ggplot2 to include the description of animation. It does this by providing a range of new grammar classes that can be added to the plot object in order to customise how it should change with time.
transition_() defines how the data should be spread out and how it relates to itself across time.
view_() defines how the positional scales should change along the animation.
**shadow_() defines how data from other points in time should be presented in the given point in time.
enter_()/exit_*() defines how new data should appear and how old data should disappear during the course of the animation.
ease_aes()** defines how different aesthetics should be eased during transitions.
8.1 Static Bubble Plot
The basic ggplot2 functions creates a static bubble plot.
ggplot(globalPop, aes(x = Old, y = Young,
size = Population,
colour = Country)) +
geom_point(alpha = 0.7,
show.legend = FALSE) +
scale_colour_manual(values = country_colors) +
scale_size(range = c(2, 12)) +
labs(title = 'Year: {frame_time}', x = '% Aged', y = '% Young')+
theme_minimal()
8.2 Animated bubble plot
transition_time() of gganimate is used to create transition through distinct states in time (i.e. Year). Other transitions include transition_components(), transition_events(), transition_filter(), transition_layers(), transition_manual(), transition_null(), transition_reveal(), transition_states()
ease_aes() is used to control easing of aesthetics. The default is linear. Other methods are: quadratic, cubic, quartic, quintic, sine, circular, exponential, elastic, back, and bounce.
plot9 <- ggplot(globalPop, aes(x = Old, y = Young,
size = Population,
colour = Country))+
geom_point(alpha = 0.7, show.legend = FALSE)+
scale_colour_manual(values = country_colors)+
scale_size(range = c(2, 12)) +
labs(title = 'Year: {frame_time}', x = '% Aged', y = '% Young')+
transition_time(Year)+
ease_aes('linear')+
theme_minimal()
plot9_anim <- animate(plot9, nframes = 30, fps = 4)
anim_save("plot9_anim.gif", plot9_anim)
plot9_anim
To adjust the speed of animation, animate() contains fps argument.
Artificially stretch the required timeline so that it is multiple years long. Scale the transition length to the numerical difference between each state.
plot10 <- ggplot(globalPop, aes(x = Old, y = Young,
size = Population,
colour = Country))+
geom_point(alpha = 0.7, show.legend = FALSE)+
scale_colour_manual(values = country_colors)+
scale_size(range = c(2, 12)) +
labs(title = 'Year: {frame_time}', x = '% Aged', y = '% Young')+
transition_time(Year)+
ease_aes('linear')+
theme_minimal()
plot10_anim <- animate(plot10, nframes = 30, fps = 10)
anim_save("plot10_anim.gif", plot10_anim)
plot10_anim
9. Animated Data Visualisation: plotly
In Plotly R package, both ggplotly() and plot_ly() support key frame animations through the frame argument/aesthetic. They also support an ids argument/aesthetic to ensure smooth transitions between objects with the same id (which helps facilitate object constancy).
9.1 Animated bubble plot: ggplotly()
Appropriate ggplot2 functions are used to create a static bubble plot. The output is then saved as an R object called gg. ggplotly() is then used to convert the R graphic object into an animated svg object.
gg <- ggplot(globalPop,
aes(x = Old,
y = Young,
size = Population,
colour = Country)) +
geom_point(aes(size = Population,
frame = Year),
alpha = 0.7,
show.legend = FALSE) +
scale_colour_manual(values = country_colors) +
scale_size(range = c(2, 12)) +
labs(x = '% Aged',
y = '% Young')+
theme_minimal()
ggplotly(gg)Although show.legend = FALSE argument was used, the legend still appears on the plot. To overcome this problem, theme(legend.position=‘none’) should be used.
gg <- ggplot(globalPop,
aes(x = Old,
y = Young,
size = Population,
colour = Country)) +
geom_point(aes(size = Population,
frame = Year),
alpha = 0.7) +
scale_colour_manual(values = country_colors) +
scale_size(range = c(2, 12)) +
labs(x = '% Aged',
y = '% Young') +
theme(legend.position='none')+
theme_minimal()
ggplotly(gg)9.2 Animated bubble plot: plot_ly()
bp <- globalPop %>%
plot_ly(x = ~Old,
y = ~Young,
size = ~Population,
color = ~Continent,
sizes = c(2, 100),
frame = ~Year,
text = ~Country,
hoverinfo = "text",
type = 'scatter',
mode = 'markers'
) %>%
layout(showlegend = FALSE)
bp